package com.unlcn.ils.wms.backend.service.inbound.impl;

import cn.huiyunche.commons.exception.BusinessException;
import cn.huiyunche.commons.utils.HttpRequestUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.unlcn.ils.wms.backend.bo.inboundBO.WmsFecthInboundFromTmsBO;
import com.unlcn.ils.wms.backend.enums.*;
import com.unlcn.ils.wms.backend.service.inbound.WmsCQInboundService;
import com.unlcn.ils.wms.base.dto.WmsCqInboundTmsDTO;
import com.unlcn.ils.wms.base.mapper.extmapper.WmsDepartureRegisterExtMapper;
import com.unlcn.ils.wms.base.mapper.inbound.WmsCqinboundTmsLogMapper;
import com.unlcn.ils.wms.base.mapper.inbound.WmsCqinboundTmsMapper;
import com.unlcn.ils.wms.base.mapper.outbound.WmsDepartureRegisterMapper;
import com.unlcn.ils.wms.base.model.inbound.WmsCqinboundTms;
import com.unlcn.ils.wms.base.model.inbound.WmsCqinboundTmsExample;
import com.unlcn.ils.wms.base.model.inbound.WmsCqinboundTmsLog;
import com.unlcn.ils.wms.base.model.outbound.WmsDepartureRegister;
import com.unlcn.ils.wms.base.model.sys.SysUser;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 重庆库入库业务相关V2
 */
@Service
public class WmsCQInboundServiceImpl implements WmsCQInboundService {

    private Logger LOGGER = LoggerFactory.getLogger(WmsCQInboundServiceImpl.class);

    @Value("${tms.pickup.host.url}")
    private String propertyUrl;

    @Value("${tms.pickup.host.timeoutfortracking}")
    private String propertyTime;

    @Value("${tms.encode.key}")
    private String propertyKey;

    @Autowired
    private WmsCqinboundTmsMapper wmsCqinboundTmsMapper;
    @Autowired
    private WmsCqinboundTmsLogMapper wmsCqinboundTmsLogMapper;
    @Autowired
    private WmsDepartureRegisterMapper wmsDepartureRegisterMapper;
    @Autowired
    private WmsDepartureRegisterExtMapper wmsDepartureRegisterExtMapper;


    //调用tms接口查询入库指令,对接道闸接口及业务--自动调用tms
    @Override
    public HashMap<String, Object> saveInboundDetailByVehicleFromTms(String vehicle, String oldVehicle, String username) {
        LOGGER.info("GateControllerServiceImpl.saveInboundDetailByVehicleFromTms param:{},{},{}", vehicle, oldVehicle, username);

        if (StringUtils.isBlank(oldVehicle)) {
            throw new BusinessException("传入的车牌号为空!");
        }
        HashMap<String, Object> resultMap = Maps.newHashMap();
        //因为先在本地查询了对应车牌的备料信息,到此方法已经是不存在数据于备料明细中.需要从tms接口调用数据
        //不能直接查询接口表中的数据,因为不知道指令号。一个板车可能进出场多次绑定了不同的指令需要区别对待
        String result = null;
        try {
            //调用Tms接口查入库指令
            result = callTms(vehicle, WmsFetchFromTmsTypeEnum.INBOUND.getValue(), WhCodeEnum.UNLCN_XN_CQ.getText());
        } catch (Exception e) {
            LOGGER.error("WmsCQInboundServiceImpl.saveInboundDetailByVehicleFromTms error:{}", e);
            resultMap.put("success", "false");
            resultMap.put("message", "调用tms接口超时失败!");
            result = "{\n" +
                    "    \"success\": false,\n" +
                    "    \"records\": null,\n" +
                    "    \"message\": \"调用tms接口超时失败!\"\n" +
                    "}";
            insertFailedData(username, oldVehicle, result);
        }
        if (StringUtils.isNotBlank(result)) {
            JSONObject jsonObject = JSONObject.parseObject(result);
            String msg = jsonObject.getString("message");
            String records = jsonObject.getString("records");
            Boolean success = jsonObject.getBoolean("success");
            resultMap.put("success", String.valueOf(success));
            resultMap.put("message", "闸门开启");
            if (!success) {
                LOGGER.error("WmsCQInboundServiceImpl.saveInboundDetailByVehicleFromTms error msg: {}", msg);
                //不抛出异常用于 闸门口异常信息显示
                resultMap.put("message", msg);
                insertFailedData(username, oldVehicle, result);
            } else {
                List<WmsFecthInboundFromTmsBO> parseArray = JSONArray.parseArray(records, WmsFecthInboundFromTmsBO.class);
                //插入数据到入库道闸业务表
                if (CollectionUtils.isNotEmpty(parseArray)) {
                    insertWmsCqInboundTms(null, parseArray, WmsSysSourceEnum.TMS.getValue());
                    insertTmsLogAndTime(oldVehicle, result);
                    WmsDepartureRegister departureRegister = insertWmsDepartureRegister(username, oldVehicle);
                    departureRegister.setDispatchNo(parseArray.get(0).getShipno());
                    departureRegister.setDrDepartureType(WmsDepartureTypeEnum.TMS_SUCCESS.getValue());
                    departureRegister.setDrOpenStatus(WmsDepartureOpenStatusEnum.OPENED.getValue());
                    departureRegister.setWhCode(WhCodeEnum.UNLCN_XN_CQ.getValue());
                    wmsDepartureRegisterMapper.insertSelective(departureRegister);
                }
                //解析数据为空
                if (CollectionUtils.isEmpty(parseArray)) {
                    LOGGER.error("WmsCQInboundServiceImpl.saveInboundDetailByVehicleFromTms error msg: {}", "调用tms未查询到对应车牌号数据");
                    resultMap.put("success", "false");
                    resultMap.put("message", "调用tms未查询到对应车牌号数据");
                    result = "{\n" +
                            "    \"success\": false,\n" +
                            "    \"records\": null,\n" +
                            "    \"message\": \"调用tms未查询到对应车牌号数据!\"\n" +
                            "}";
                    insertFailedData(username, oldVehicle, result);
                }
            }
        }
        return resultMap;
    }


    /**
     * 用于进行入库手动开闸门的记录--手动
     *
     * @param departureRegister 板车出入场记录
     * @param dto               板车车牌号
     * @param sysUser           用户id
     * @param whCode            仓库code
     * @throws Exception 抛出异常
     */
    @Override
    public void addInboundOpenByHm(WmsDepartureRegister departureRegister, WmsCqInboundTmsDTO dto, SysUser sysUser, String whCode) throws Exception {
        LOGGER.info("WmsCQInboundServiceImpl.addInboundOpenByHm param:{},{},{},{}", departureRegister, dto, sysUser, whCode);
        if (Objects.equals(dto, null))
            throw new BusinessException("参数不能为空!");
        if (StringUtils.isBlank(dto.getDrId()))
            throw new BusinessException("记录id不能为空!");
        if (StringUtils.isBlank(whCode))
            throw new BusinessException("仓库code不能为空!");
        if (!WhCodeEnum.UNLCN_XN_CQ.getValue().equals(whCode))
            throw new BusinessException("该仓库不支持此操作!");
        //后台继续调用tms接口表
        List<WmsCqinboundTms> wmsCqinboundTms = getLastWmsCqinboundTms(departureRegister.getDrVehiclePlate());
        if (CollectionUtils.isNotEmpty(wmsCqinboundTms)) {
            WmsCqinboundTms tms = wmsCqinboundTms.get(0);
            if (String.valueOf(Boolean.FALSE).equalsIgnoreCase(tms.getTmsResult()) && tms.getTmsInnvocationCount() <= CALL_TMS_COUNT) {
                tms.setWmsInTime(new Date());
                tms.setGmtUpdate(new Date());
                if (sysUser != null) {
                    tms.setModifyUserName(sysUser.getName());
                }
                wmsCqinboundTmsMapper.updateByPrimaryKeySelective(tms);
                //再将数据传至TMS
                ExecutorService service = null;
                try {
                    service = Executors.newSingleThreadExecutor();
                    service.execute(() -> {
                        String result = null;
                        try {
                            result = callTms(departureRegister.getDrVehiclePlate(), WmsFetchFromTmsTypeEnum.INBOUND.getValue(), WhCodeEnum.UNLCN_XN_CQ.getText());
                        } catch (Exception e) {
                            //再定时的调用tms  也可以使用延迟线程池定时执行
                            updateScheduleCallTms(departureRegister, sysUser);
                        }
                        if (StringUtils.isNotBlank(result)) {
                            JSONObject jsonObject = JSONObject.parseObject(result);
                            String msg = jsonObject.getString("message");
                            String records = jsonObject.getString("records");
                            Boolean success = jsonObject.getBoolean("success");
                            if (success) {
                                //更新数据为成功
                                List<WmsFecthInboundFromTmsBO> parseArray = JSONArray.parseArray(records, WmsFecthInboundFromTmsBO.class);
                                if (CollectionUtils.isNotEmpty(parseArray)) {
                                    insertWmsCqInboundTms(sysUser, parseArray, WmsSysSourceEnum.WMS_CREATE.getValue());
                                    //更新出入场记录表的指令号
                                    departureRegister.setDispatchNo(parseArray.get(0).getShipno());
                                    wmsDepartureRegisterMapper.updateByPrimaryKeySelective(departureRegister);
                                }
                                //保存对应的日志表
                                insertTmsLogAndTime(dto.getVehicle(), result);
                            } else {
                                LOGGER.error("WmsCQInboundServiceImpl.addInboundOpenByHm error", msg);
                                //再调用tms
                                updateScheduleCallTms(departureRegister, sysUser);
                            }
                        }
                    });
                    service.shutdown();
                } catch (Exception e) {
                    LOGGER.error("WmsCQInboundServiceImpl.addInboundOpenByHm 手动开启闸门失败 error:", e);
                } finally {
                    if (service != null) {
                        service.shutdownNow();
                    }
                }
            }
        }
    }

    /**
     * 调用接口失败记录
     *
     * @param username 用户名
     * @param result   调用tms接口的返回结果
     */
    private void insertFailedData(String username, String oldVehicle, String result) {
        Runnable runnable = () -> {
            //去除重复
            HashMap<String, Object> paramMap = Maps.newHashMap();
            paramMap.put("vehicle", oldVehicle);
            paramMap.put("inOut", WmsGateControllerTypeEnum.GATE_IN.getCode());
            paramMap.put("notOpen", WmsDepartureOpenStatusEnum.NOT_OPEN.getValue());
            paramMap.put("result", WmsDepartureTypeEnum.TMS_FAILED.getValue());
            List<WmsDepartureRegister> list = wmsDepartureRegisterExtMapper.selectTodayData(paramMap);
            if (CollectionUtils.isEmpty(list)) {
                WmsCqinboundTms tms = new WmsCqinboundTms();
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                //构建数据,插入数据到本地接口表
                tms.setAtSendBusinessFlag(String.valueOf(SendBusinessFlagEnum.SEND_N.getValue()));
                tms.setAtSysSource(WmsSysSourceEnum.WMS_CREATE.getValue());
                tms.setWhCode(WhCodeEnum.UNLCN_XN_CQ.getValue());
                tms.setWhName(WhCodeEnum.UNLCN_XN_CQ.getText());
                tms.setIsDelete(DeleteFlagEnum.NORMAL.getValue());
                tms.setCreateUserName(username);
                tms.setModifyUserName(username);
                tms.setGmtCreate(new Date());
                tms.setGmtUpdate(new Date());
                tms.setWmsInOutStatus(WmsGateControllerTypeEnum.GATE_IN.getCode());
                tms.setTmsInnvocationCount(1);
                tms.setTmsResult(String.valueOf(Boolean.FALSE));
                tms.setSupplierVehiclePlate(oldVehicle);
                wmsCqinboundTmsMapper.insertSelective(tms);
                //插入到闸门记录表
                WmsDepartureRegister departureRegister = insertWmsDepartureRegister(username, oldVehicle);
                departureRegister.setWhCode(WhCodeEnum.UNLCN_XN_CQ.getValue());
                wmsDepartureRegisterMapper.insertSelective(departureRegister);
            }
            //插入日志
            insertTmsLogAndTime(oldVehicle, result);
        };
        new Thread(runnable).start();
    }

    /**
     * 插入数据到出入场登记表
     *
     * @param username 用户名
     */
    private WmsDepartureRegister insertWmsDepartureRegister(String username, String oldVehicle) {
        WmsDepartureRegister gate = new WmsDepartureRegister();
        gate.setGmtUpdate(new Date());
        gate.setGmtCreate(new Date());
        gate.setDrVehiclePlate(oldVehicle);
        gate.setInOutType(WmsGateControllerTypeEnum.GATE_IN.getCode());
        gate.setDrDepartureType(WmsDepartureTypeEnum.TMS_FAILED.getValue());
        gate.setDrOpenStatus(WmsDepartureOpenStatusEnum.NOT_OPEN.getValue());
        gate.setCreateUserName(username);
        gate.setModifyUserName(username);
        return gate;
    }

    /**
     * 查询最后一条调用tms的闸门记录
     *
     * @param vehicle 车牌号
     * @return 返回值
     */
    private List<WmsCqinboundTms> getLastWmsCqinboundTms(String vehicle) {
        WmsCqinboundTmsExample tmsExample = new WmsCqinboundTmsExample();
        tmsExample.setOrderByClause("id desc");
        tmsExample.setLimitStart(0);
        tmsExample.setLimitEnd(1);
        tmsExample.createCriteria().andIsDeleteEqualTo(DeleteFlagEnum.NORMAL.getValue())
                .andWmsInOutStatusEqualTo(WmsGateControllerTypeEnum.GATE_IN.getCode())
                .andSupplierVehiclePlateEqualTo(vehicle)
                .andAtSysSourceEqualTo(WmsSysSourceEnum.WMS_CREATE.getValue());
        return wmsCqinboundTmsMapper.selectByExample(tmsExample);
    }


    private final int CALL_TMS_COUNT = 5;

    /**
     * 再计划的调用tms接口
     *
     * @param departureRegister 出入场记录表
     * @param sysUser           用户
     */
    private void updateScheduleCallTms(WmsDepartureRegister departureRegister, SysUser sysUser) {
        ScheduledExecutorService scheduledExecutorService = null;
        try {
            scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
            //开启定时线程调用tms
            ScheduledExecutorService finalScheduledExecutorService = scheduledExecutorService;
            scheduledExecutorService.scheduleAtFixedRate(() -> {
                List<WmsCqinboundTms> lastWmsCqinboundTms = getLastWmsCqinboundTms(departureRegister.getDrVehiclePlate());
                if (CollectionUtils.isNotEmpty(lastWmsCqinboundTms)) {
                    WmsCqinboundTms tms = lastWmsCqinboundTms.get(0);
                    if (!Objects.equals(tms, null) && tms.getTmsInnvocationCount() < CALL_TMS_COUNT) {
                        tms.setId(null);
                        tms.setGmtUpdate(new Date());
                        tms.setGmtCreate(new Date());
                        if (sysUser != null) {
                            tms.setCreateUserName(sysUser.getName());
                            tms.setModifyUserName(sysUser.getName());
                        }
                        tms.setTmsInnvocationCount(tms.getTmsInnvocationCount() + 1);
                        String result = null;
                        try {
                            result = callTms(departureRegister.getDrVehiclePlate(), WmsFetchFromTmsTypeEnum.INBOUND.getValue(), WhCodeEnum.UNLCN_XN_CQ.getText());
                        } catch (Exception e) {
                            LOGGER.error("WmsCQInboundServiceImpl.updateScheduleCallTms error:", e);
                            result = "{\n" +
                                    "    \"success\": false,\n" +
                                    "    \"records\": null,\n" +
                                    "    \"message\": \"调用tms接口超时失败!\"\n" +
                                    "}";
                        }
                        if (StringUtils.isNotBlank(result)) {
                            JSONObject jsonObject = JSONObject.parseObject(result);
                            String msg = jsonObject.getString("message");
                            String records = jsonObject.getString("records");
                            Boolean success = jsonObject.getBoolean("success");
                            if (success) {
                                //更新数据为成功
                                tms.setTmsResult(String.valueOf(Boolean.TRUE));
                                List<WmsFecthInboundFromTmsBO> parseArray = JSONArray.parseArray(records, WmsFecthInboundFromTmsBO.class);
                                if (CollectionUtils.isNotEmpty(parseArray)) {
                                    insertWmsCqInboundTms(sysUser, parseArray, WmsSysSourceEnum.WMS_CREATE.getValue());
                                    departureRegister.setDispatchNo(parseArray.get(0).getShipno());
                                    wmsDepartureRegisterMapper.updateByPrimaryKeySelective(departureRegister);
                                }

                            } else {
                                LOGGER.error("WmsCQInboundServiceImpl.updateScheduleCallTms error", msg);
                                //再调用tms 4次
                                result = "{\n" +
                                        "    \"success\": false,\n" +
                                        "    \"records\": null,\n" +
                                        "    \"message\": \"" + msg + "!\"\n" +
                                        "}";
                                tms.setTmsResult(String.valueOf(Boolean.FALSE));
                            }
                        }
                        wmsCqinboundTmsMapper.insertSelective(tms);
                        //保存对应的日志表
                        insertTmsLogAndTime(departureRegister.getDrVehiclePlate(), result);
                    } else if (!Objects.equals(tms, null) && tms.getTmsInnvocationCount() >= CALL_TMS_COUNT) {
                        finalScheduledExecutorService.shutdownNow();
                    }
                }
            }, 0, 2, TimeUnit.MINUTES);
        } catch (Exception e) {
            LOGGER.error("WmsCQInboundServiceImpl.updateScheduleCallTms 执行失败 error:", e);
            if (scheduledExecutorService != null) {
                scheduledExecutorService.shutdownNow();
            }
        }
    }


    /**
     * 保存调用tms入库道闸的业务表
     *
     * @param sysUser    用户名
     * @param parseArray 数组
     * @param sysSource  数据来源--tms调用成功的数据  或者手动开闸后再调用tms成功的数据
     */
    private void insertWmsCqInboundTms(SysUser sysUser, List<WmsFecthInboundFromTmsBO> parseArray, String sysSource) {
        Runnable runnable = () -> parseArray.forEach((WmsFecthInboundFromTmsBO v) -> {
            WmsCqinboundTms tms = new WmsCqinboundTms();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            //构建数据,插入数据到本地接口表
            tms.setAtSendBusinessFlag(String.valueOf(SendBusinessFlagEnum.SEND_N.getValue()));
            tms.setAtSysSource(sysSource);
            tms.setCreateUserName(v.getCustomer());
            tms.setCustomerName(v.getCustomer());
            tms.setCustomerOrderNo(v.getOrderno());
            tms.setDest(v.getDest());//调度单号对应tms调度指令号
            tms.setDispatchNo(v.getShipno());
            tms.setWhCode(WhCodeEnum.UNLCN_XN_CQ.getValue());
            tms.setWhName(WhCodeEnum.UNLCN_XN_CQ.getText());
            tms.setWaybillNo(v.getCustshipno());
            try {
                Date dtShip = sdf.parse(v.getDtship());
                if (!Objects.equals(dtShip, null)) {
                    tms.setDtshipTime(dtShip);
                }
            } catch (ParseException e) {
                e.printStackTrace();
            }
            tms.setVin(v.getVin());
            tms.setVehicleStyle(v.getStyle());
            tms.setVehicleStyleDesc(v.getStyleDesc());
            tms.setSupplierVehiclePlate(v.getVehicle());
            tms.setSupplierName(v.getSupplier());
            tms.setSupplierDriverName(v.getDriver());
            tms.setSupplierDriverMobile(v.getMobile());
            tms.setRouteEnd(v.getRoute_end());
            tms.setOrigin(v.getOrigin());
            tms.setIsDelete(DeleteFlagEnum.NORMAL.getValue());
            if (sysUser != null) {
                tms.setCreateUserName(sysUser.getName());
                tms.setModifyUserName(sysUser.getName());
            }
            tms.setGmtCreate(new Date());
            tms.setGmtUpdate(new Date());
            tms.setWmsInTime(new Date());
            tms.setWmsInOutStatus(WmsGateControllerTypeEnum.GATE_IN.getCode());
            tms.setTmsInnvocationCount(1);
            tms.setTmsResult(String.valueOf(Boolean.TRUE));
            wmsCqinboundTmsMapper.insertSelective(tms);
        });
        new Thread(runnable).start();
    }

    /**
     * 保存tms的日志和时间
     *
     * @param vehicle 用户名
     * @param result  tms的返回结果
     */

    private void insertTmsLogAndTime(String vehicle, String result) {
        //记录tms接口结果到wms_cqinbound_tms_log 表中
        Runnable runnable = () -> {
            if (StringUtils.isNotBlank(result)) {
                SimpleDateFormat milSdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
                JSONObject jsonObject = JSONObject.parseObject(result);
                String msg = jsonObject.getString("message");
                String records = jsonObject.getString("records");
                Boolean success = jsonObject.getBoolean("success");
                WmsCqinboundTmsLog log = new WmsCqinboundTmsLog();
                log.setCreateTime(new Date());
                log.setTmsInOutType(WmsGateControllerTypeEnum.GATE_IN.getCode());
                log.setTmsInvocationTimestamp(Long.valueOf(milSdf.format(new Date())));
                log.setTmsMsg(msg);
                log.setTmsParam(vehicle);
                log.setTmsResult(String.valueOf(success));
                log.setTmsRecords(records);
                log.setTmsUrl(propertyUrl + "/mShipSearch.jspx");
                wmsCqinboundTmsLogMapper.insertSelective(log);
            }
        };
        new Thread(runnable).start();
    }

    /**
     * 调用 tms 的接口
     *
     * @param vehicle 板车车牌
     * @param type    出入库类型
     * @return 返回值
     */

    private String callTms(String vehicle, String type, String whname) {
        // 构建URL
        String url = propertyUrl;
        Integer time = Integer.parseInt(propertyTime);
        String encode_key = propertyKey;
        Map<String, Object> map = new HashMap<>();
        Map<String, Object> headerMap = new HashMap<>();
        headerMap.put("encode-key", encode_key);
        url = url + "/mShipSearch.jspx";
        //设置时间戳
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //就以当前时间向前推60天作为时间戳
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(new Date());//当前时间
        calendar.add(Calendar.DATE, -60);
        Date date = calendar.getTime();
        String timestamp = sdf.format(date);
        map.put("timestamp", timestamp);
        map.put("warehouse", whname);
        map.put("vehicle", vehicle);
        map.put("type", type);
        String result = null;
        try {
            result = HttpRequestUtil.sendHttpPost(url, headerMap, map, time);
        } catch (Exception e) {
            throw new BusinessException("调用tms接口超时失败!");
        }
        return result;
    }

}
